/*
*    Phantom v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include "stdafx.h"
#include "minionmanager.h"
#include "minionlib.h"
#include "MinionLib_InternalErrors.h"

CMinionManager::CMinionManager(void)
: RemoteThread(0)
{
	MapPtr = 0;
	MapPtr_0 = 0;
}

CMinionManager::~CMinionManager(void)
{
}

SSI_INT64 CMinionManager::AddDataMap(SSI_INT64 RemoteType, SSI_INT64 LocalType)
{
	return DataMap.AddMap(RemoteType, LocalType);
}


CStatus CMinionManager::Initialize(SSI_INT64 In_ThreadID)
{
	// Set the hook
	if(In_ThreadID){  // Enter 0 hook for test mode
		RemoteThread = In_ThreadID;
		if(!SetMinionHook(RemoteThread, GetCustomProc())){
			M_MAKEERR0(ERR_M01100);
		}
	}

	// Create the map pointer
	if(MapPtr_0){
		MemoryMap.UnMap(MapPtr_0);
	}
	MapPtr = (char*)MemoryMap.MakePointer();
	MapPtr_0 = MapPtr;
	if(!MapPtr){
		M_MAKEERR0(ERR_M01101);
	}

	return STAT_OK;
}

CStatus CMinionManager::DoFunction(SSI_INT64 lParam)
{
	ClearMinionSync();
	// Sync the data types
	PostThreadMessage(RemoteThread, WM_COMMAND, MINION_SETINTTYPE, (LPARAM)CDataInt::GetRegisteredType());
	PostThreadMessage(RemoteThread, WM_COMMAND, MINION_SETSTRINGTYPE, (LPARAM)CDataString::GetRegisteredType());
	PostThreadMessage(RemoteThread, WM_COMMAND, MINION_SETBOOLTYPE, (LPARAM)CDataBool::GetRegisteredType());
	PostThreadMessage(RemoteThread, WM_COMMAND, MINION_SETREALTYPE, (LPARAM)CDataReal::GetRegisteredType());
	PostThreadMessage(RemoteThread, WM_COMMAND, MINION_SETVOIDTYPE, (LPARAM)CDataVoid::GetRegisteredType());  /// MUST BE LAST
	PostThreadMessage(RemoteThread, WM_COMMAND, MINION_DOFUNCTION, (LPARAM)lParam);
	return STAT_OK;
}

CStatus CMinionManager::ReadResponse(SSI_FunctionAPI& theAPI)
{
	SSI_FunctionAPI newAPI;

	if(!WaitForSync(1000)){
		//P_MAKEERR0(ERR_P00301);  // Internal Error
		//MinionLog("No Sync Flag");
		M_MAKEERR0(ERR_M01102);
	}else{
		// Read the new API
		if(!ReadFunctionAPI(newAPI)){
			M_MAKEERR0(ERR_M01103);
		}
		theAPI.m_ReturnStatus = newAPI.m_ReturnStatus;
		theAPI.SetReturnData(newAPI.GetReturnData());
	}
	return STAT_OK;
}

CStatus CMinionManager::Cleanup(void)
{
	ClearMinionSync(); 
	// Unmap the pointer
	ClearMap();
	// Remove the hook

	if(RemoteThread){
		SetMinionHook(RemoteThread, 0);
	}

	return STAT_OK;
}

CData* CMinionManager::ReadDataObject(){
	SSI_INT64 Type = 0;
	SSI_INT64 StringSize = 0;
	CData* Ret = 0;

	memcpy(&Type, MapPtr, sizeof(Type));
	MapPtr += sizeof(Type);

	// Map it to the type in this local process
	Type = DataMap.GetLocalType(Type);

	if(Type == CDataString::GetRegisteredType()){
		memcpy(&StringSize, MapPtr, sizeof(StringSize));
		MapPtr += sizeof(StringSize);
		char* str = new char[StringSize+1];
		memcpy(str, MapPtr, StringSize);
		MapPtr += StringSize;
		str[StringSize] = 0;
		Ret = Factory_MakeData(Type);
		if(!Ret){
			delete [] str;
			return 0;
		}
		((CDataString*)Ret)->Data = str;
		delete [] str;
		return Ret;
	}else if(Type == CDataInt::GetRegisteredType()){
		SSI_INT64 Data;
		memcpy(&Data, MapPtr, sizeof(Data));
		MapPtr += sizeof(Data);
		Ret = Factory_MakeData(Type);
		((CDataInt*)Ret)->Data = Data;
		return Ret;
	}else if(Type == CDataReal::GetRegisteredType()){
		REAL Data;
		memcpy(&Data, MapPtr, sizeof(Data));
		MapPtr += sizeof(Data);
		Ret = Factory_MakeData(Type);
		((CDataReal*)Ret)->Data = Data;
		return Ret;
	}else if(Type == CDataBool::GetRegisteredType()){
		SSI_BOOL Data;
		memcpy(&Data, MapPtr, sizeof(Data));
		MapPtr += sizeof(Data);
		Ret = Factory_MakeData(Type);
		((CDataBool*)Ret)->Data = Data;
		return Ret;
	}else if(Type == CDataVoid::GetRegisteredType()){
		// Return an empty void
		Ret = Factory_MakeData(Type);
		return Ret;
	}else{
		//MinionLog("Data Type Error");
		return 0;
	}
	return 0;
}

int CMinionManager::WriteDataObject(CData* In){
	SSI_INT64 RemoteType = 0;
	SSI_INT64 Type = In->GetType();
	SSI_INT64 StringSize = 0;

	// Map it to the type in this local process
	RemoteType = DataMap.GetRemoteType(Type);

	memcpy(MapPtr, &RemoteType, sizeof(RemoteType));
	MapPtr += sizeof(RemoteType);

	if(Type == CDataString::GetRegisteredType()){
		StringSize = ((CDataString*)In)->Data.GetLength();
    if(StringSize >= (MAP_FILE_SIZE - (MapPtr-MapPtr_0) - 1000)){
      StringSize = (MAP_FILE_SIZE - (MapPtr-MapPtr_0) - 1000);
    }
		memcpy(MapPtr, &StringSize, sizeof(StringSize));
		MapPtr += sizeof(StringSize);
		memcpy(MapPtr, (void*)(((CDataString*)In)->Data.Str()), StringSize);
		MapPtr += StringSize;
		return 1;
	}else if(Type == CDataInt::GetRegisteredType()){
		SSI_INT64 Data = ((CDataInt*)In)->Data;
		memcpy(MapPtr, &Data, sizeof(Data));
		MapPtr += sizeof(Data);
		return 1;
	}else if(Type == CDataReal::GetRegisteredType()){
		REAL Data = ((CDataReal*)In)->Data;
		memcpy(MapPtr, &Data, sizeof(Data));
		MapPtr += sizeof(Data);
		return 1;
	}else if(Type == CDataBool::GetRegisteredType()){
		SSI_BOOL Data = ((CDataBool*)In)->Data;
		memcpy(MapPtr, &Data, sizeof(Data));
		MapPtr += sizeof(Data);
		return 1;
	}else if(Type == CDataVoid::GetRegisteredType()){
		// Write Nothing
		return 1;
	}else{
		return 0;
	}
	return 0;
}

int CMinionManager::WriteFunctionAPI(SSI_FunctionAPI& theAPI){
	CVariable* aVar;
	CData* aData;
	int ctr = 0;
	SSI_INT64 Version = theAPI.GetVersion();
	SSI_INT64 NumParams = theAPI.GetNumParams();
	SSI_INT64 StringSize;

	memcpy(MapPtr, &Version, sizeof(Version));
	MapPtr += sizeof(Version);

	memcpy(MapPtr, &NumParams, sizeof(NumParams));
	MapPtr += sizeof(NumParams);

	StringSize = theAPI.m_FunctionName.GetLength();
	memcpy(MapPtr, &StringSize, sizeof(StringSize));
	MapPtr += sizeof(StringSize);
	
	memcpy(MapPtr, theAPI.m_FunctionName.Str(), theAPI.m_FunctionName.GetLength());
	MapPtr += StringSize;

	// Write the Return Value
	aVar = theAPI.GetReturnData();
	if(!aVar) return 0;
	aData = &aVar->GetWorkingData();
	if(!aData) return 0;
	if(!WriteDataObject(aData)){
		return 0;
	}
	
	//Read the Return Status
	SSI_INT64 RetState = theAPI.m_ReturnStatus.GetState();
	memcpy(MapPtr, &RetState, sizeof(RetState));
	MapPtr += sizeof(RetState);
	
	CStatus aStat = theAPI.m_ReturnStatus;
	String s = aStat.GetStatusMessage();
	StringSize = s.GetLength();
	memcpy(MapPtr, &StringSize, sizeof(StringSize));
	MapPtr += sizeof(StringSize);
	
	memcpy(MapPtr, s.Str(), StringSize);
	MapPtr += StringSize;

	while(ctr < NumParams){
		aVar = theAPI[ctr];  
		if(!aVar) return 0;
		aData = &aVar->GetWorkingData();
		if(!aData) return 0;
		if(!WriteDataObject(aData)){
			return 0;
		}
		ctr++;
	}
	return 1;
}

int CMinionManager::ReadFunctionAPI(SSI_FunctionAPI& theAPI){
	CData* aData;

	SSI_INT64 ctr = 0;
	SSI_INT64 Version = 0;
	SSI_INT64 NumParams = 0;
	SSI_INT64 StringSize;
	SSI_INT64 RetState = 0;

	memcpy(&Version, MapPtr, sizeof(Version));
	MapPtr += sizeof(Version);
	memcpy(&NumParams, MapPtr, sizeof(NumParams));
	MapPtr += sizeof(NumParams);

	memcpy(&StringSize, MapPtr, sizeof(StringSize));
	MapPtr += sizeof(StringSize);
	
	char* str = new char[StringSize+1];
	memcpy(str, MapPtr, StringSize);
	MapPtr += StringSize;
	str[StringSize] = 0;
	theAPI.m_FunctionName = str;
	delete [] str;

	// Read the Return Value
	aData = ReadDataObject();
	if(!aData){
		return 0;
	}

	theAPI.GetReturnData()->SetType(aData->Type);
	CStatus aStat;
	if(!(aStat = theAPI.SetReturnData(aData))){
    delete aData;
		return 0;
		//MessageBox(SSI_NULL, aStat.GetStatusMessage().Str(), "Info", MB_OK);
	}

	// Because a copy is made in SetReturnData
	if(aData){
		delete aData;
		aData = 0;
	}

	// Read the Return Status
	memcpy(&RetState, MapPtr, sizeof(RetState));
	MapPtr += sizeof(RetState);
	
	memcpy(&StringSize, MapPtr, sizeof(StringSize));
	MapPtr += sizeof(StringSize);

	str = new char[StringSize+1];
	memcpy(str, MapPtr, StringSize);
	MapPtr += StringSize;
	str[StringSize] = 0;
	theAPI.m_ReturnStatus.Set((SSI_BYTE)RetState, str);
	delete [] str;

	theAPI.InitParams(NumParams);
	while(ctr < NumParams){
		CVariable aVar;
		
		aData = ReadDataObject();
		if(!aData) return 0;
		aVar.SetType(aData->Type);
		aVar.SetData(aData);

		// Because SetData makes a copy
		if(aData){
		  delete aData;
		  aData = 0;
		}

		if(!theAPI.AddParameter(aVar, ctr)){
			return 0;
		}
		ctr++;
	}
	return 1;
}


void CMinionManager::MakeMap(void){
	if(MapPtr_0){
		MemoryMap.UnMap(MapPtr_0);
	}
	MapPtr = (char*)MemoryMap.GetPointer();
	MapPtr_0 = MapPtr;
}

void CMinionManager::ResetMap(void)
{
	//if(MapPtr_0){
	//	MemoryMap.UnMap(MapPtr_0);
	//}
	//MapPtr = (char*)MemoryMap.GetPointer();
	//MapPtr_0 = MapPtr;
	MapPtr = MapPtr_0;
}

void CMinionManager::ClearMap(void)
{
	if(MapPtr_0){
		MemoryMap.UnMap(MapPtr_0);
	}
	MapPtr = 0;
	MapPtr_0 = 0;

}

void CMinionManager::InitLocalMap(void)
{
	DataMap.AddMap(CDataInt::GetRegisteredType(), CDataInt::GetRegisteredType());
	DataMap.AddMap(CDataBool::GetRegisteredType(), CDataBool::GetRegisteredType());
	DataMap.AddMap(CDataString::GetRegisteredType(), CDataString::GetRegisteredType());
	DataMap.AddMap(CDataReal::GetRegisteredType(), CDataReal::GetRegisteredType());
	DataMap.AddMap(CDataVoid::GetRegisteredType(), CDataVoid::GetRegisteredType());
}
